package com.zc.component.network.http.client;

import com.zc.component.network.core.*;
import io.vertx.core.Vertx;
import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.client.WebClientOptions;
import lombok.SneakyThrows;
import com.zc.web.bean.FastBeanCopier;
import com.zc.core.metadata.ConfigMetadata;
import com.zc.core.metadata.DefaultConfigMetadata;
import com.zc.core.metadata.types.BooleanType;
import com.zc.core.metadata.types.IntType;
import com.zc.core.metadata.types.ObjectType;
import com.zc.core.metadata.types.StringType;
import com.zc.component.network.core.security.CertificateManager;
import com.zc.component.network.core.security.VertxKeyCertTrustOptions;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.net.URL;

/**
 * Http Client 服务上,使用Vertx实现HttpClient
 *
 */
@Component
public class HttpClientProvider implements NetworkProvider<HttpClientConfig> {

    private final CertificateManager certificateManager;

    private final Vertx vertx;

    public HttpClientProvider(CertificateManager certificateManager, Vertx vertx) {
        this.certificateManager = certificateManager;
        this.vertx = vertx;
    }

    /**
     * 获取网络组件类型
     * @return 网络组件类型
     */
    @Nonnull
    @Override
    public NetworkType getType() {
        return DefaultNetworkType.HTTP_CLIENT;
    }

    /**
     * 使用配置创建一个网络组件
     * @param properties 配置信息
     * @return Http 客户端
     */
    @Nonnull
    @Override
    public VertxHttpClient createNetwork(@Nonnull HttpClientConfig properties) {
        VertxHttpClient client = new VertxHttpClient(properties.getId());
        client.setWebClient(createHttpClient(properties));
        client.setClientConfig(properties);
        return client;
    }

    /**
     * 重新加载网络组件
     * @param network    网络组件
     * @param properties 配置信息
     */
    @Override
    public void reload(@Nonnull Network network, @Nonnull HttpClientConfig properties) {
        VertxHttpClient client = ((VertxHttpClient) network);
        client.setWebClient(createHttpClient(properties));
        client.setClientConfig(properties);
    }

    /**
     * 创建 Http 客户端
     * @param config Http Client配置信息
     * @return Http 客户端
     */
    protected WebClient createHttpClient(HttpClientConfig config) {
        // 创建 WebClientOptions 实例并进行配置
        WebClientOptions options = new WebClientOptions();
        // 设置连接超时时间为30秒
        options.setConnectTimeout(config.getRequestTimeout());
        // 是否启用SSL
        if (config.isSsl()) {
            // 是否启用SSL
            options.setSsl(config.isSsl());
            // 是否验证远程服务的host
            options.setVerifyHost(config.isVerifyHost());
            // 是否信任所有证书
            options.setTrustAll(config.isTrustAll());
            // 证书信息临时变量
            if (config.getCertificate() != null) {
                VertxKeyCertTrustOptions keyCertTrustOptions = new VertxKeyCertTrustOptions(config.getCertificate());
                if (!config.isTrustAll()) {
                    options.setTrustOptions(keyCertTrustOptions);
                }
                if (config.getCertificate().getKeyManagerFactory() != null) {
                    options.setKeyCertOptions(keyCertTrustOptions);
                }
            }
        }
        return WebClient.create(vertx, options);

    }

    /**
     * @return 配置定义元数据
     */
    @Nullable
    @Override
    public ConfigMetadata getConfigMetadata() {
        return new DefaultConfigMetadata()
            .add("id", "id", "", new StringType())
            .add("instance", "服务实例数量(线程数)", "", new IntType())
            .add("certId", "证书id", "", new StringType())
            .add("ssl", "是否开启ssl", "", new BooleanType())
            .add("port", "请求服务端口", "", new IntType())
            .add("httpHeaders", "请求头", "", new ObjectType())
            .add("port", "请求服务端口", "", new IntType())
            .add("host", "请求服务主机地址", "", new StringType())
            .add("baseUrl", "请求路径前缀", "", new StringType())
            .add("verifyHost", "是否验证主机", "", new BooleanType())
            .add("trustAll", "是否信任所有", "", new BooleanType())
            .add("requestTimeout", "请求超时时间", "", new IntType());

    }

    /**
     * 根据可序列化的配置信息创建网络组件配置
     *
     * @param properties 原始配置信息
     * @return 网络配置信息
     */
    @Nonnull
    @Override
    public Mono<HttpClientConfig> createConfig(@Nonnull NetworkProperties properties) {

        return Mono.defer(() -> {
            HttpClientConfig config = FastBeanCopier.copy(properties.getConfigurations(), new HttpClientConfig());
            config.setId(properties.getId());
            setAddress(config);
            if (config.isSsl() && StringUtils.hasText(config.getCertId())) {
                return certificateManager.getCertificate(config.getCertId())
                                         .doOnNext(config::setCertificate)
                                         .thenReturn(config);
            }
            return Mono.just(config);
        });
    }

    @SneakyThrows
    private void setAddress(HttpClientConfig config) {
        if (StringUtils.hasText(config.getBaseUrl()) && !config.getBaseUrl().startsWith("http")) {
            config.setBaseUrl("https://".concat(config.getBaseUrl()));
        }
        if (StringUtils.hasText(config.getBaseUrl())) {
            URL url = new URL(config.getBaseUrl());
            config.setHost(url.getHost());
            config.setPort(url.getPort());
        }
    }
}
